/*
 * @Author: Shaohan Weng 
 * @Email: shaohanweng@outlook.com
 * @Description:
 * VI -> VPSS -> IVE 通路
 * 注意，查阅手册获得如下关键信息：
 * IVE Thresh 的最大处理分辨率为 1920x1080，而 IMX335 摄像头输入分辨率为 2592x1944 (4:3)
 * 因此必须先缩放，再交给 IVE 处理
 * 同时，Hi3516DV300 的硬件限制，VPSS 的 0 通道不能缩放，必须用 1 通道缩放
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/prctl.h>
#include "ive_common.h"
#include "sample_comm.h"

static HI_BOOL s_bStopSignal = HI_FALSE;
static pthread_t s_hThreshThread = 0;
static SAMPLE_VI_CONFIG_S s_stViConfig = {0};

int count = 0;

/* 
* IVE Thresh 处理线程
* 1. HI_MPI_VPSS_GetChnFrame 从 VPSS 通道拿到视频帧，数据类型为 VIDEO_FRAME_INFO_S
* 2. 创建两个 IVE 图像 stSrc 和 stDst，数据类型为 IVE_DATA_S
*    其中，stSrc 即视频帧，而 stDst 调用 API 从媒体内存池创建
* 3. HI_MPI_IVE_Thresh 处理图像二值化，将处理结果写入 stDst
* 4. 处理完后务必释放以上所有数据，否则内存将很快耗尽
*/

static HI_VOID *IVE_ThreshProc(HI_VOID *pArgs)
{
    HI_S32 s32Ret;
    while (HI_FALSE == s_bStopSignal) {

        HI_S32 s32VpssGrp = 0;
        HI_S32 as32VpssChn[] = {VPSS_CHN0, VPSS_CHN1};
        VIDEO_FRAME_INFO_S stBaseFrmInfo;
        HI_S32 s32MilliSec = SAMPLE_IVE_FRM_TIMEOUT;

        s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp, as32VpssChn[1], &stBaseFrmInfo, s32MilliSec);
        SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, RELEASE,
            "Error(%#x),HI_MPI_VPSS_GetChnFrame failed, VPSS_GRP(%d), VPSS_CHN(%d)!\n", s32Ret, s32VpssGrp,
            as32VpssChn[0]);

        IVE_DATA_S stSrc;
        stSrc.u64VirAddr = stBaseFrmInfo.stVFrame.u64VirAddr[0];
        stSrc.u64PhyAddr = stBaseFrmInfo.stVFrame.u64PhyAddr[0];
        stSrc.u32Stride = stBaseFrmInfo.stVFrame.u32Stride[0];
        stSrc.u32Width = stBaseFrmInfo.stVFrame.u32Width;
        stSrc.u32Height = stBaseFrmInfo.stVFrame.u32Height;


        IVE_DST_IMAGE_S stDstImage;
        s32Ret = SAMPLE_COMM_IVE_CreateImage(&stDstImage, IVE_IMAGE_TYPE_U8C1, 1920, 1080);
        SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, RELEASE, "Error(%#x),Create src image failed!\n", s32Ret);

        IVE_DATA_S stDst;
        stDst.u64VirAddr = stDstImage.au64VirAddr[0];
        stDst.u64PhyAddr = stDstImage.au64PhyAddr[0];
        stDst.u32Stride = stBaseFrmInfo.stVFrame.u32Stride[0];
        stDst.u32Width = stBaseFrmInfo.stVFrame.u32Width;
        stDst.u32Height = stBaseFrmInfo.stVFrame.u32Height;

        // IVE_HANDLE IveHandle;
        // HI_BOOL bInstant = HI_FALSE;
        // IVE_DMA_CTRL_S stDmaCtrl = { IVE_DMA_MODE_DIRECT_COPY, 0, 0, 0, 0 };
        // s32Ret = HI_MPI_IVE_DMA(&IveHandle, &stSrc, &stDst, &stDmaCtrl, bInstant);
        // SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, RELEASE, "Error(%#x),HI_MPI_IVE_DMA failed!\n", s32Ret);

        // // HI_BOOL bInstant = HI_TRUE;
        // // IVE_IMAGE_S pstSrc;
        // // IVE_IMAGE_S pstDst;
        // // s32Ret = SAMPLE_COMM_IVE_DmaImage(&stBaseFrmInfo, &pstSrc, bInstant);
        // // SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, BASE_RELEASE, "SAMPLE_COMM_IVE_DmaImage fail,Error(%#x)\n",
        // //         s32Ret);
        // // s32Ret = SAMPLE_COMM_IVE_CreateImage(&pstDst, IVE_IMAGE_TYPE_U8C1, 800, 480);
        // // SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, BASE_RELEASE, "SAMPLE_COMM_IVE_CreateImage fail,Error(%#x)\n",
        // //         s32Ret);
        
        IVE_HANDLE IveHandle;
        HI_BOOL bInstant = HI_FALSE;
        IVE_THRESH_CTRL_S stThrCtrl;
        stThrCtrl.u8LowThr=128;
        stThrCtrl.u8HighThr=0;
        stThrCtrl.enMode=IVE_THRESH_MODE_BINARY;
        stThrCtrl.u8MaxVal=255;
        stThrCtrl.u8MinVal=0;
        s32Ret = HI_MPI_IVE_Thresh(&IveHandle, &stSrc, &stDst, &stThrCtrl, bInstant);


        // TODO: 可以用Canny边缘检测进一步优化

        /* 以下步骤需要引入 OpenCV 和C/C++混编，尚未调通，保留注释和关键API：

        1. 色彩空间转换：YUV420SP 到 BGR
        IVE_CSC_CTRL_S stCSCCtrl = {IVE_CSC_MODE_PIC_BT601_YUV2RGB};
        ret = HI_MPI_IVE_CSC(&iveHnd, src, dst, &stCSCCtrl, HI_TRUE);
        int ImgRgbToBgr(IVE_IMAGE_S *img)

        2. memcpy 拷贝 MMZ 缓存池数据到 CV Mat

        3. 求图像的中心距（计算目标颜色区域的中心坐标）
        vector<Moments>mu(contours.size());
        for (int i = 0; i < contours.size(); i++)
        {
            mu[i] = moments(contours[i], false);
        }

        计算图像的质心
        vector<Point2f>mc(contours.size());
        for (int i = 0; i < contours.size(); i++)
        {
            mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
        }

        计算目标颜色中心坐标和图像中心坐标在横向的偏差

        4. 将偏差作为最终向Hi3861发布的数据，通过串口通信

        */

    RELEASE:
        IVE_MMZ_FREE(stSrc.u64PhyAddr, stSrc.u64VirAddr);
        IVE_MMZ_FREE(stDst.u64PhyAddr, stDst.u64VirAddr);

        s32Ret = HI_MPI_VPSS_ReleaseChnFrame(s32VpssGrp, as32VpssChn[0], &stBaseFrmInfo);
        if (s32Ret != HI_SUCCESS) {
            SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n", s32Ret, s32VpssGrp,
                as32VpssChn[0]);
        }

    }

    return HI_NULL;
}

static HI_S32 IVE_ThreshPause(HI_VOID)
{
    printf("---------------press Enter key to exit!---------------\n");
    if (s_bStopSignal == HI_TRUE) {
        if (s_hThreshThread != 0) {
            pthread_join(s_hThreshThread, HI_NULL);
            s_hThreshThread = 0;
        }

        StopViVpssVo(&s_stViConfig);
        printf("\033[0;31mprogram termination abnormally!\033[0;39m\n");
        return HI_FAILURE;
    }
    (HI_VOID)getchar();
    if (s_bStopSignal == HI_TRUE) {
        if (s_hThreshThread != 0) {
            pthread_join(s_hThreshThread, HI_NULL);
            s_hThreshThread = 0;
        }

        StopViVpssVo(&s_stViConfig);
        printf("\033[0;31mprogram termination abnormally!\033[0;39m\n");
        return HI_FAILURE;
    }
    return HI_SUCCESS;
}

/* MPP VI->VPSS->IVE Thersh 二值化处理通路 */
HI_VOID IVE_Thresh(HI_VOID)
{
    SIZE_S stSize;
    PIC_SIZE_E enSize = PIC_1080P;
    HI_S32 s32Ret = HI_SUCCESS;

    /* step 1: start vi vpss vo */
    s32Ret = StartViVpssVo(&s_stViConfig, &enSize);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, END, "Error(%#x),StartViVpssVo failed!\n",
        s32Ret);

    s32Ret = SAMPLE_COMM_SYS_GetPicSize(enSize, &stSize);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, END, "Error(%#x),SAMPLE_COMM_SYS_GetPicSize failed!\n", s32Ret);

    /* step 3: Create work thread */
    s_bStopSignal = HI_FALSE;
    s32Ret = prctl(PR_SET_NAME, "IVE_ThreshProc", 0, 0, 0);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, END, "thread set name failed!\n");
    s32Ret = pthread_create(&s_hThreshThread, NULL, IVE_ThreshProc, NULL);
    SAMPLE_CHECK_EXPR_GOTO(s32Ret != HI_SUCCESS, END, "thread create failed!\n");

    s32Ret = IVE_ThreshPause();
    SAMPLE_CHECK_EXPR_RET_VOID(s32Ret != HI_SUCCESS, "pause failed!\n");

    s_bStopSignal = HI_TRUE;
    pthread_join(s_hThreshThread, HI_NULL);
    s_hThreshThread = 0;

END:
    s_bStopSignal = HI_TRUE;
    s_hThreshThread = 0;
    StopViVpssVo(&s_stViConfig);
    return;
}

/* function : Thresh sample signal handle */
HI_VOID IVE_Thresh_HandleSig(HI_VOID)
{
    s_bStopSignal = HI_TRUE;
}
